#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/md5.h>
#include <glib.h>
#include <dlfcn.h>
#include <time.h>

#include "dict.h"
#include "date.h"
#include "encrypt.h"
#include "kylin-ukey.h"
#include "kylin-activation.h"
#include "kylin-verify.h"
#include "misc.h"

#include "kylin-license.h"

#include "type.h"

#include "ukey-macro.h"

#ifdef ENABLE_FYKEY
#include "skfapi.h"


/*
定义 方圆Ukey 的普通用户空间大小、偏移量,admin用户空间偏移量
*/
#define BUF_SIZE                    8192
#define	FANGYUAN_USER_SPACE_OFFSET	0
#define	FANGYUAN_ADMIN_SPACE_OFFSET	0
#define	FANGYUAN_FILE_SPACE_SIZE	    8192

extern char* _kylin_activation_get_register_number(const char* serial_no, int* result);
static int _fykey_used_register_count(int space_size);

static int USER_SPACE = BUF_SIZE;

/*
龙脉Ukey 存储区容量大小
*/
#define LINE_SIZE 256
#define MAX_LENGTH 4096
#define KEY_FILE_SIZE 512
#define FILENAME_SIZE 256

#define LICENSE_GROUP "license"
#define SPACE_ALIGNMENT 1000

#define ENCRYPTED_NUMBER_LENGTH 20

/*
定义 国密库 路径、md5信息
*/
#if defined __x86_64 || defined __x86_64__ || defined __amd64 || defined __amd64__
#define FYKEY_SHARED_LIBRARY_PATH        "/usr/lib/x86_64-linux-gnu/libSKFAPI20361.so"
#define	FYKEY_SHARED_LIBRARY_MD5SUM		 "b81e07d10fd9d3f93d2fa0e8288384d6"
#define	FYKEY_SHARED_LIBRARY_SHA1SUM	 "e1e043d6ca08971dd98b4e7d860eb02c7fbbf671"
#define	FYKEY_SHARED_LIBRARY_SHA256SUM	 "7e6f160de8563f649818257e4dd55170e7bf3a7632ccd34c82140f32b112478e"
#endif

#if defined __i386 || defined __i386__ || defined __i686 || defined __i686__
#define	FYKEY_SHARED_LIBRARY_PATH		"/usr/lib/i386-linux-gnu/libSKFAPI20361.so"
#define	FYKEY_SHARED_LIBRARY_MD5SUM		"ae3b55ae7b401fb1713b7009e5073db2"
#define	FYKEY_SHARED_LIBRARY_SHA1SUM		"9aa7f4f7e2e59b33f06cc324506452b34de5e8d7"
#define	FYKEY_SHARED_LIBRARY_SHA256SUM		"949967eaafb575ecd0ec533628866e0afd96979f58182744ced104169d2d116d"
#endif

#ifdef __aarch64__
#define	FYKEY_SHARED_LIBRARY_PATH		"/usr/lib/aarch64-linux-gnu/libSKFAPI20361.so"
#define	FYKEY_SHARED_LIBRARY_MD5SUM		"697ae584a7f79e5e811236e2a793d5f0"
#define	FYKEY_SHARED_LIBRARY_SHA1SUM		"54b489f83937afb1fcbd29dff68fd1bac01149f1"
#define	FYKEY_SHARED_LIBRARY_SHA256SUM		"d1c3acb2d5c30df46677e44e1c275564067b2fd6adcbc63351a9f9e5cabc05a9"
#endif

#ifdef __mips__
  #ifdef __LP64__
    #define FYKEY_SHARED_LIBRARY_PATH "/usr/lib/mips64el-linux-gnuabi64/libSKFAPI20361.so"
  #endif
#endif


//global variables in this file
bool bLicenseExistfy = false;
bool bActivateExistfy = false;
static unsigned char fykey_license_data[BUF_SIZE] = {0};
static unsigned char fykey_activation_data[BUF_SIZE] = {0};
static char dump_message[BUF_SIZE];
static GKeyFile* fykey_license_keyfile = NULL;
static GKeyFile* fykey_activation_keyfile = NULL;
static int has_already_get = 0;

/* 保存设备列表信息每个设备的名称以单个‘\0’结束，以双‘\0’表示列表的结束。*/
char		 szDevNameListfy[0x400] = {0};
char	szFileNamefy[FILENAME_SIZE] = {0};

/* 保存设备打开时句柄 */
DEVHANDLE					 hDevfy = NULL;
HANDLE 						 happfy = NULL;

/* 设备数 */
static int			 gDongleCount = 0;

/*
 * init longmai key environment
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_init();

/*
 * check if library is valid
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_check_library();

static char* fykey_get_user_pin(DEVINFO *pDevInfo);

static int fykey_get_data_force();

/* 定义 fykey 函数指针 */
// 枚举加密锁
DWORD (*my_FykeyEnum)(BOOL bPresent, LPSTR szNameList, ULONG *pulSize);
// 连接加密锁
DWORD (*my_FykeyConnect)(LPSTR szName, DEVHANDLE *phDev);
// 断连加密锁
DWORD (*my_FykeyClose)(DEVHANDLE hDevfy);
// 获取加密锁信息
DWORD (*my_FykeyGetDevInfo)(DEVHANDLE hDevfy, DEVINFO *pDevInfo);
// 枚举应用
DWORD (*my_FykeyEnumApp)(DEVHANDLE hDevfy, LPSTR szAppName, ULONG *pulSize);
// 打开应用
DWORD (*my_FykeyOpenApp)(DEVHANDLE hDevfy, LPSTR szAppName, HAPPLICATION *phApplication);
// 关闭应用
DWORD (*my_FykeyCloseApp)(HAPPLICATION hApplication);
// SKF_VerifyPIN
DWORD (*my_FykeyVerifyPIN)(HAPPLICATION hApplication, ULONG  ulPINType, LPSTR szPIN, ULONG *pulRetryCount);
// SKF_EnumFiles
DWORD (*my_FykeyEnumFiles)(HAPPLICATION hApplication, LPSTR szFileList, ULONG *pulSize);
// SKF_GetFileInfo
DWORD (*my_FykeyGetFileInfo)(HAPPLICATION hApplication, LPSTR szFileNamefy, FILEATTRIBUTE *pFileInfo);
// 读数据
DWORD (*my_FykeyReadFile)(HAPPLICATION hApplication, LPSTR szFileNamefy, ULONG ulOffset, ULONG ulSize, BYTE * pbOutData, ULONG *pulOutLen);
// 写数据
DWORD (*my_FykeyWriteFile)(HAPPLICATION hApplication, LPSTR szFileNamefy, ULONG ulOffset, BYTE *pbData, ULONG ulSize);

/*
 * load longmai key library
 * return NO_ERROR if succeed, or error number if not
 */
int fykey_load_library()
{
	void* handle;
	char* error;
	int ret;

	ret = fykey_check_library();
	if(!ret)
		return UKEY_LIBRARY_LOAD_ERROR;

	if (0 != access(FYKEY_SHARED_LIBRARY_PATH, F_OK))
		return NO_ERROR;

	// 加载 /usr/lib/{arch}-linux-gnu/libEsStdMidware.so
	handle = dlopen(FYKEY_SHARED_LIBRARY_PATH, RTLD_NOW);
	if(!handle)
		return UKEY_LIBRARY_LOAD_ERROR;

	// 枚举加密锁
	my_FykeyEnum = dlsym(handle, "SKF_EnumDev");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 连接加密锁
	my_FykeyConnect = dlsym(handle, "SKF_ConnectDev");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 断连加密锁
	my_FykeyClose = dlsym(handle, "SKF_DisConnectDev");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 获取加密锁信息
	my_FykeyGetDevInfo = dlsym(handle, "SKF_GetDevInfo");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 枚举应用
	my_FykeyEnumApp = dlsym(handle, "SKF_EnumApplication");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 打开应用
	my_FykeyOpenApp = dlsym(handle, "SKF_OpenApplication");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 关闭应用
	my_FykeyCloseApp = dlsym(handle, "SKF_CloseApplication");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 验证权限
	my_FykeyVerifyPIN = dlsym(handle, "SKF_VerifyPIN");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 枚举文件
	my_FykeyEnumFiles = dlsym(handle, "SKF_EnumFiles");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

    // 获取文件信息
	my_FykeyGetFileInfo = dlsym(handle, "SKF_GetFileInfo");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 读数据
	my_FykeyReadFile = dlsym(handle, "SKF_ReadFile");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 写数据
	my_FykeyWriteFile = dlsym(handle, "SKF_WriteFile");
	if((error = dlerror()) != NULL) {
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	return NO_ERROR;
}

/*
 * init longmai key environment
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_init()
{
	int                 iRet = NO_ERROR;
	DWORD count;
    char	szAppName[256] = {0};
    
    char*   pappname = szAppName;
    char	*pUserPin = "0x123";
    FILEATTRIBUTE *pFileInfo;
    DEVINFO pDevInfo;

    ULONG	ulAppNameLen = 256;
	ULONG	ulRetryCount = 0;
    ULONG	pulSize = 256;
    LPSTR szFileList;

    iRet = my_FykeyEnumApp(hDevfy, szAppName, &ulAppNameLen);
    if( iRet != SAR_OK ) {
        iRet = UKEY_ENUM_APP_ERROR;
        goto fykey_init_out;
    }
	
	iRet = my_FykeyOpenApp(hDevfy, pappname, &happfy);
    if( iRet != SAR_OK ) {
        iRet = UKEY_OPEN_APP_ERROR;
        goto fykey_init_out;
    }

    iRet = my_FykeyGetDevInfo(hDevfy, &pDevInfo);
    if( iRet != SAR_OK ) {
        iRet = UKEY_GET_DEVINFO_ERROR;
        goto fykey_init_out;
    }

    // pUserPin = fykey_get_user_pin(&pDevInfo);

    // pUserPin = "EERXEKMDVQpHYrBD";

	// iRet = my_FykeyVerifyPIN(happfy, USER_TYPE, pUserPin, &ulRetryCount);
    // if( iRet != SAR_OK ) {
    //     iRet = UKEY_USERPIN_VERIFY_ERROR;
    //     goto fykey_init_out;
    // }

    iRet = my_FykeyEnumFiles(happfy, szFileNamefy, &pulSize);
    if( iRet != SAR_OK ) {
        iRet = UKEY_ENUM_FILES_ERROR;
        goto fykey_init_out;
    }

    // iRet = my_FykeyGetFileInfo(happfy, szFileNamefy, pFileInfo);
    // if( iRet != SAR_OK ) {
    //     iRet = UKEY_USERPIN_VERIFY_ERROR;
    //     goto fykey_init_out;
    // }

	fykey_get_data_force();

fykey_init_out:
	return iRet;
}

/*
 * find longmai key
 * return NO_ERROR if feitian is inserted, or error number if not
 */
int fykey_find(DWORD* count)
{
	int                 iRet = NO_ERROR;
    ULONG				ulResult = SAR_OK;
	ULONG				ulDevNameListLen;

	if (0 != access(FYKEY_SHARED_LIBRARY_PATH, F_OK)) {
		iRet = NO_UKEY_FOUND; //没有设备
        goto fykey_find_out;
	}
    
    //枚举设备获得设备名
	ulDevNameListLen = sizeof(szDevNameListfy);
	ulResult = my_FykeyEnum(TRUE, szDevNameListfy, &ulDevNameListLen);
    if ( SAR_OK != ulResult || ulDevNameListLen <= 0 ) {
        iRet = NO_UKEY_FOUND; //没有设备
        goto fykey_find_out;
    }

    //连接设备，默认连接第一个设备
	ulResult = my_FykeyConnect(szDevNameListfy, &hDevfy);
	if ( SAR_OK != ulResult ) {
        iRet = NO_UKEY_FOUND; //没有设备
        goto fykey_find_out;
    }

	*count = ulDevNameListLen;

	/*  test  */
	fykey_init();

fykey_find_out:
	return iRet;
}

/*
 * get key hid
 * return key hid string on success, or NULL if failed
 */
char* fykey_get_hid()
{
	DWORD ret;
	DWORD count;
	DEVINFO info = {0};
	memset(&info, 0, sizeof(info));

	char buf[BUF_SIZE] = {0};

	ret = fykey_find(&count);
	if(ret != NO_ERROR)
		return NULL;

	ret = my_FykeyGetDevInfo(hDevfy, &info);
	if( ret != SAR_OK ) {
		printf("Get device infomation error.%08x\n", ret);
		return NULL;
	} else {
		return strdup(info.SerialNumber);
	}

	return NULL;
}

/*
 * get data
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_get_data(int force)
{
	DWORD ret;
    ULONG pulOutLen = 8192;
	char* str = NULL;
	char* delim = "\0";
	char sFileName[BUF_SIZE] = { 0 };
	char* szFileList[FILENAME_SIZE] = {0};
	int i = 0, current_size = 0;
	char *szFileNameCpy = szFileNamefy;
	GList* glFileList = NULL;

	if(!force && has_already_get)
		return NO_ERROR;

	char *tmp=NULL;

	while( i<FILENAME_SIZE && szFileNamefy[i] != '\0' ) {
		tmp = &szFileNamefy[i];
		i += strlen(tmp) + 1;
		glFileList = g_list_append(glFileList, tmp);
	}

	for(GList *l = glFileList; l != NULL; l = l->next) {
		if(!l->data)
			continue;

		tmp = (char*)l->data;
		if(strncmp(tmp, "license", strlen("license")) == 0)
			bLicenseExistfy = true;
		if(strncmp(tmp, "activation_data", strlen("activation_data")) == 0)
			bActivateExistfy = true;
	}

	if (!bLicenseExistfy || !bActivateExistfy) {
		return NO_ERROR;
	}

	ret = my_FykeyReadFile(happfy, "license", FANGYUAN_USER_SPACE_OFFSET, BUF_SIZE, fykey_license_data, &pulOutLen);
    if ( SAR_OK != ret ) {
        has_already_get = 0;
        ret = UKEY_GET_DATA_ERROR; //没有设备
        goto get_data_out;
    }

	ret = my_FykeyReadFile(happfy, "activation_data", FANGYUAN_USER_SPACE_OFFSET, BUF_SIZE, fykey_activation_data, &pulOutLen);
    if ( SAR_OK != ret ) {
        has_already_get = 0;
        ret = UKEY_GET_DATA_ERROR; //没有设备
        goto get_data_out;
    }

	has_already_get = 1;

	if( fykey_license_keyfile == NULL || fykey_activation_keyfile == NULL )
		fykey_refresh_keyfile((const char*)fykey_license_data, (const char*)fykey_activation_data);

get_data_out:
	return NO_ERROR;
}

/*
 * force to get data in feitian
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_get_data_force()
{
	DWORD ret;
	DWORD count;
	
	// ret = fykey_find(&count);
	// if(ret != NO_ERROR)
	// 	return ret;

	return fykey_get_data(1);
}

int fykey_refresh_keyfile(const char *license_data, const char *activation_data)
{
	gboolean res;
	GKeyFileFlags flags;
	GError* error = NULL;
	GKeyFile* license_keyfile = NULL,* activation_keyfile = NULL;

	license_keyfile = g_key_file_new();
	if(!license_keyfile)
		return UKEY_KEY_FILE_ERROR;

	flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;

	res = g_key_file_load_from_data(license_keyfile, license_data, strlen(license_data), flags, &error);
	if(!res) {
		g_key_file_free(license_keyfile);
		fykey_license_keyfile = NULL;
		return UKEY_KEY_FILE_ERROR;
	}

	fykey_license_keyfile = license_keyfile;

	activation_keyfile = g_key_file_new();
	if(!activation_keyfile)
		return UKEY_KEY_FILE_ERROR;

	res = g_key_file_load_from_data(activation_keyfile, activation_data, strlen(activation_data), flags, &error);
	if(!res) {
		g_key_file_free(activation_keyfile);
		fykey_activation_keyfile = NULL;
		return UKEY_KEY_FILE_ERROR;
	}
	fykey_activation_keyfile = activation_keyfile;

	return NO_ERROR;
}

/*
 *  write data to longmai key
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_write_data(unsigned char* buf, size_t offset, size_t length)
{
	DWORD ret;

	ret = fykey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_FykeyWriteFile(happfy, "activation_data", offset, buf, length);
	if(SAR_OK != ret)
		return UKEY_WRITE_DATA_ERROR;

	return NO_ERROR;
}

/*
 * add a register code into longmai key
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_add_register_code(const char* register_code, int* _offset)
{
	DWORD ret;
	unsigned short aligned_space = 0;
	int offset = -1;
	char buf[BUF_SIZE];

	if(!register_code_is_valid(register_code))
		return INVALID_REGISTER_CODE;

	// ret = fykey_init();
	// if(ret != NO_ERROR)
	// 	return ret;

	// ret = fykey_get_data_force();
	// if(ret != NO_ERROR)
	// 	return ret;

	// ret = kylin_get_license(&fykey_license_keyfile, (const char*)&fykey_license_data);
	// if(ret != NO_ERROR)
	// 	return ret;

	memset(buf, 0, sizeof(buf));
	memcpy(buf, fykey_activation_data, BUF_SIZE);

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
	offset = kylin_check_register_code_space_validation(buf, USER_SPACE);

	if(offset >= aligned_space)
		return UKEY_SPACE_FULL;

	ret = fykey_write_data((unsigned char*)register_code, offset, ENCRYPTED_NUMBER_LENGTH);
	if(ret != NO_ERROR)
		return ret;

	if(_offset)
		*_offset = offset;

	return NO_ERROR;
}

/*
 * check if longmai key space is full
 * return 1 if full, or 0 if not
 */
static int fykey_space_is_full(const char* buf, const size_t size)
{
	int aligned_space;
	int offset = 0;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
	offset = kylin_check_register_code_space_validation(buf, size);

	if(offset < aligned_space)
		return 0;

	return 1;
}

static int fykey_check_remain_times(const char* buf, const size_t size)
{
	int aligned_space;
	int offset = 0;
	int module_count = 0;
	int used_count = 0;

	module_count = kylin_get_license_count(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);
	used_count = _fykey_used_register_count(module_count * ENCRYPTED_NUMBER_LENGTH);

	if(used_count < module_count)
		return NO_ERROR;

	return UKEY_MODULE_NO_MORE_COUNT;
}

/*
 * check if longmai key library is valid
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_check_library()
{
#if 0
	return (file_check_md5(ESKEY_SHARED_LIBRARY_PATH, FTKEY_SHARED_LIBRARY_MD5SUM)  &&
	        file_check_sha1(ESKEY_SHARED_LIBRARY_PATH, FTKEY_SHARED_LIBRARY_SHA1SUM) &&
		file_check_sha256(ESKEY_SHARED_LIBRARY_PATH, FTKEY_SHARED_LIBRARY_SHA256SUM));
#else
	return 1;
#endif
}

/*
 * activate system with longmai key
 * return NO_ERROR if succeed, or error number if not
 * 将被外面调用的函数
 */
int fykey_activate_system(const char* license_serial, 
			  const char* kyinfo_ukey, const char* expire_date, 
			  encrypted_number_generator ukey_raw_generator)
{
	DWORD ret = -1;
	char* ukey_serial = NULL;
	char* ukey_expire = NULL;
	char* ukey_expire_no_hyphen = NULL;
	char* raw = NULL;
	char* ukey_hid = NULL;
	int register_code_in_ukey = 0;
	int offset = -1;
	int aligned_space = -1;
	int result = 0;
	char* register_code = NULL;
	int license_serial_mode = SERIAL_MODE_UNKNOWN;
	int ukey_serial_mode = SERIAL_MODE_UNKNOWN;
	char* ukey_produce = NULL;


	//printf("init\n");
	ret = fykey_init();
	if(ret != NO_ERROR)
		return ret;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);

	//printf("get data\n");
	ret = fykey_get_data_force();
	if(ret != NO_ERROR)
		goto fykey_activate_system_out;

	if( fykey_license_keyfile == NULL || fykey_activation_keyfile == NULL )
		fykey_refresh_keyfile((const char*)fykey_license_data, (const char*)fykey_activation_data);

	//printf("get license key file\n");
	// ret = kylin_get_license(&fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);
	// if(ret != NO_ERROR)
	// 	goto fykey_activate_system_out;

	//printf("get license serial\n");
	ukey_serial = kylin_get_license_serial(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);
	if(!ukey_serial) {
		ret = UKEY_NO_SERIAL;
		goto fykey_activate_system_out;
	}

	ukey_produce = kylin_get_license_produce(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);

	ukey_serial_mode = _serial_number_mode(ukey_serial);
	if((SERIAL_MODE_UNKNOWN == ukey_serial_mode) || (SERIAL_MODE_BATCH == ukey_serial_mode)) {
		ret = UKEY_SERIAL_NUMBER_VALIDATION_ERROR;
		goto fykey_activate_system_out;
	}

	if(SERIAL_MODE_LEGACY == ukey_serial_mode) {
		if(strcmp(license_serial, ukey_serial) != 0) {
			ret = SERIAL_NUMBER_MATCH_ERROR;
			goto fykey_activate_system_out;
		}
	}

	license_serial_mode = _serial_number_mode(license_serial);
	if((SERIAL_MODE_LEGACY == license_serial_mode) || (SERIAL_MODE_NEW == license_serial_mode)) {
		if(strcmp(license_serial, ukey_serial) != 0) {
			ret = SERIAL_NUMBER_MATCH_ERROR;
			goto fykey_activate_system_out;
		}
	} else if(SERIAL_MODE_BATCH == license_serial_mode) {
		if(ukey_produce) {
			if(strcmp(license_serial, ukey_produce) != 0) {
				ret = PRODUCE_NUMBER_MATCH_ERROR;
				goto fykey_activate_system_out;
			}
		}

		if(!_product_type_check(license_serial, ukey_serial)) {
			ret = PRODUCT_TYPE_MATCH_ERROR;
			goto fykey_activate_system_out;
		}
	} else {
		ret = LICENSE_SERIAL_NUMBER_VALIDATION_ERROR;
		goto fykey_activate_system_out;
	}

	//printf("get expire time\n");
	ukey_expire = kylin_get_license_expire(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);
	if(!ukey_expire) {
		ret = UKEY_NO_EXPIRE_TIME;
		goto fykey_activate_system_out;
	}

	//printf("expire time remove hyphen\n");
	ukey_expire_no_hyphen = code_remove_hyphen(ukey_expire);
	if(!ukey_expire_no_hyphen) {
		ret = UKEY_EXPIRE_DEAL_HYPHEN_ERROR;
		goto fykey_activate_system_out;
	}

	ukey_hid = fykey_get_hid();
	if(!ukey_hid) {
		ret = UKEY_GET_HID_ERROR;
		goto fykey_activate_system_out;
	}

	//printf("generate ukey raw\n");
	if(!ukey_raw_generator) {
		ret = UKEY_ACTIVATE_SYSTEM_FAILED;
		goto fykey_activate_system_out;
	}

	register_code = _kylin_activation_get_register_number(ukey_serial, &result);
	if(result != NO_ERROR) {
		ret = result;
		goto fykey_activate_system_out;
	}

	if(!register_code) {
		ret = REGISTER_CREATION_ERROR;
		goto fykey_activate_system_out;
	}

	raw = ukey_raw_generator(register_code, ukey_hid, ukey_expire_no_hyphen);
	if(!raw) {
		ret = UKEY_ACTIVATE_SYSTEM_FAILED;
		goto fykey_activate_system_out;
	}


	if(!kyinfo_ukey && !expire_date)
		goto ukey_do_activation;

	if(!kyinfo_ukey && expire_date) {
		if(_same_expire_date(ukey_expire, expire_date)) {
			ret = HAS_NORMALLY_ACTIVATED;
			goto fykey_activate_system_out;
		}
	}

	if(kyinfo_ukey && expire_date) {
		if(_same_ukey_hid(ukey_hid, kyinfo_ukey)) {
			goto ukey_do_activation;
		} else {
			if(_same_expire_date(ukey_expire, expire_date)) {
				ret = HAS_ACTIVATED_WITH_ANOTHER_UKEY;
				goto fykey_activate_system_out;
			}
			else
				goto ukey_do_activation;
		}
	}

ukey_do_activation:
	//printf("find register code\n");
	register_code_in_ukey = kylin_find_register_code(register_code, (const char*)fykey_activation_data, USER_SPACE);
	if(register_code_in_ukey) {
		ret = _kylin_activation_activate_system(raw, ukey_hid, ukey_serial, 0);
		goto fykey_activate_system_out;
	}

	if(fykey_space_is_full((const char*)fykey_activation_data, FANGYUAN_FILE_SPACE_SIZE)) {
		ret = UKEY_SPACE_FULL;
		goto fykey_activate_system_out;
	}

	//printf("activate system test\n");
	ret = _kylin_activation_activate_system_test(raw, ukey_hid, ukey_serial);
	if(ret != NO_ERROR)
		goto fykey_activate_system_out;
	
	/* Check the remaining activation times */
	ret = fykey_check_remain_times((const char*)fykey_activation_data, FANGYUAN_FILE_SPACE_SIZE);
	if(ret != NO_ERROR) {
		goto fykey_activate_system_out;
	}

	//printf("activate system\n");
	ret = _kylin_activation_activate_system(raw, ukey_hid, ukey_serial, 0);
	if(ret != NO_ERROR)
		goto fykey_activate_system_out;

	//printf("add register code\n");
	ret = fykey_add_register_code(register_code, &offset);
	if(ret != NO_ERROR)
		goto fykey_activate_system_out;

	//printf("sync data\n");
	if((-1 != offset) && (0 == (offset % ENCRYPTED_NUMBER_LENGTH)) && (offset < aligned_space))
		memcpy(&fykey_activation_data[offset], register_code, ENCRYPTED_NUMBER_LENGTH);
	else
		fykey_get_data_force();

	ret = NO_ERROR;

fykey_activate_system_out:
	if(register_code)
		free(register_code);
	if(ukey_serial)
		free(ukey_serial);
	if(ukey_expire)
		free(ukey_expire);
	if(ukey_expire_no_hyphen)
		free(ukey_expire_no_hyphen);
	if(ukey_produce)
		free(ukey_produce);
	if(raw)
		free(raw);
	if(ukey_hid)
		free(ukey_hid);

	return ret;
}

/*
 * dump longmai basic information to file f
 * return NO_ERROR if succeed, or error number if not
 */
static int fykey_dump_basic_info(FILE *f, int display)
{
	int ret = -1;
	char* ukey_hid = NULL;
	char* ukey_serial = NULL;
	char* ukey_expire = NULL;
	char* ukey_to = NULL;
	int module_count = 0;
    int used_count = 0;
    int free_count = 0;
	char* ukey_produce = NULL;

	char line[MAX_LENGTH];
	char message[MAX_LENGTH];


	ukey_hid = fykey_get_hid();
	if(!ukey_hid) {
		ret = UKEY_GET_HID_ERROR;
		goto ftkey_dump_base_out;
	}

	ukey_serial = kylin_get_license_serial(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);
	if(!ukey_serial) {
		ret = UKEY_NO_SERIAL;
		goto ftkey_dump_base_out;
	}

	ukey_expire = kylin_get_license_expire(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);
	if(!ukey_expire) {
		ret = UKEY_NO_EXPIRE_TIME;
		goto ftkey_dump_base_out;
	}

	// delete vikey_get_module

	ukey_to = kylin_get_license_to(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);

	ukey_produce = kylin_get_license_produce(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);

    module_count = kylin_get_license_count(fykey_license_keyfile, (const char*)&fykey_license_data[FANGYUAN_USER_SPACE_OFFSET]);

    if (module_count > 0) {
        used_count = _fykey_used_register_count(module_count * ENCRYPTED_NUMBER_LENGTH);
        if (used_count == 0)
            free_count = module_count;
        else if (used_count > 0)
            if (module_count > used_count)
                free_count = module_count - used_count;
    } else
        free_count = 0;

	memset(line, 0, MAX_LENGTH);
	memset(message, 0, MAX_LENGTH);


	sprintf(line, "ukeyid=%s\nserial=%s\nterm=%s\nmodule_count=%d\nused_count=%d\nfree_count=%d\n", 
		ukey_hid, ukey_serial, ukey_expire, module_count, used_count, free_count);

	strcat(message, line);

	if(ukey_to) {
		memset(line, 0, MAX_LENGTH);
		sprintf(line, "to=%s\n", ukey_to);
		strcat(message, line);
	}

	if(ukey_produce) {
		memset(line, 0, MAX_LENGTH);
		sprintf(line, "produce=%s\n", ukey_produce);
		strcat(message, line);
	}

	strcat(message, "\n");

	if(display)
		fprintf(f, "%s", message);

	strcat(dump_message, message);

	ret = NO_ERROR;

ftkey_dump_base_out:
	if(ukey_hid)
		free(ukey_hid);
	if(ukey_serial)
		free(ukey_serial);
	if(ukey_expire)
		free(ukey_expire);
	if(ukey_to)
		free(ukey_to);
	if(ukey_produce)
		free(ukey_produce);

	return ret;
}

/*
 * dump longmai key register code to file f
 * return NO_ERROR if succeed, or error number if not
 */
static void fykey_dump_register_code(FILE *f, int display)
{
	int _index = 0;
	int count = 0;
	char buf[ ENCRYPTED_NUMBER_LENGTH + 1];
	char message[MAX_LENGTH];
	char line[LINE_SIZE];

	memset(buf, 0, ENCRYPTED_NUMBER_LENGTH + 1);
	memset(message, 0, MAX_LENGTH);
	memset(line, 0, LINE_SIZE);

	count = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);

	for(_index = 0; _index < count; _index += ENCRYPTED_NUMBER_LENGTH) {
		memcpy(buf, &fykey_activation_data[_index], ENCRYPTED_NUMBER_LENGTH);
		if(register_code_is_valid(buf)) {
			sprintf(line, "%s\n", buf);
			strcat(message, line);
		}
	}

	if(display)
		fprintf(f, "%s", message);

	strcat(dump_message, message);
}

/*
 * return the number of register code which has been stored.
 */
static int _fykey_used_register_count(int space_size)
{
    int _index = 0;
    int count = 0;
    int space = 0;
    int aligned_space = 0;
    char buf[ENCRYPTED_NUMBER_LENGTH + 1] = {0};

    memset(buf, 0, ENCRYPTED_NUMBER_LENGTH + 1);

    aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
    if (space_size < aligned_space)
        space = space_size;
    else
        space = aligned_space;

    for (_index = 0; _index < space; _index += ENCRYPTED_NUMBER_LENGTH) {
        memcpy(buf, &fykey_activation_data[_index], ENCRYPTED_NUMBER_LENGTH);
        if (register_code_is_valid(buf))
            count++;
    }

    return count;
}

/*
 * dump longmai key information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
static int _fykey_dump(FILE* f, int encrypt, int display_all)
{
	DWORD ret = -1;

	int display_basic = 1;
	int display_register_code = 0;

	char* encrypted_message = NULL;
	unsigned long encrypted_message_size = -1;

	ret = fykey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = fykey_get_data_force();
	if(ret != NO_ERROR)
		return ret;

	memset(dump_message, 0, sizeof(dump_message));

	if(display_all) {
		display_basic = 1;
		display_register_code = 1;
	}

	if(encrypt) {
		display_basic = 0;
		display_register_code = 0;
	}

	ret = fykey_dump_basic_info(f, display_basic);
	if(ret != NO_ERROR)
		return ret;

	fykey_dump_register_code(f, display_register_code);

	if(encrypt) {
		ret = gpg_encrypt(dump_message, &encrypted_message, &encrypted_message_size);
		if(ret != NO_ERROR)
			return ret;

		if(encrypted_message && (encrypted_message_size > 0)) {
			fprintf(f, "%s", encrypted_message);
		}
	}

	return NO_ERROR;
}

/*
 * dump longmai key information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
int fykey_dump(FILE* f, int encrypt, int display_all)
{
	return _fykey_dump(f, encrypt, display_all);
}

/* 
*/
static char* fykey_get_user_pin(DEVINFO *pDevInfo)
{
    pDevInfo->SerialNumber;
}

#endif // ENABLE_FYKEY
